/**
 * Copyright 2020 jianggujin (www.jianggujin.com).
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0
 *  
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jianggujin.dbfly.util;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.function.Predicate;

/**
 * 链表
 * 
 * @author jianggujin
 *
 * @param <K>
 * @param <V>
 */
public class JLinkedList<K, V> implements Iterable<JLinkedEntry<K, V>> {
    private JLinkedEntry<K, V> entry = null;

    /**
     * 头部添加
     * 
     * @param key
     * @param value
     */
    public synchronized void addFirst(K key, V value) {
        JAssert.notNull(key, "key不能为空");
        JAssert.notNull(value, "value不能为空");
        if (this.entry == null) {
            this.entry = new JLinkedEntry<K, V>(key, value);
        } else if (this.hasKey0(key)) {
            throw new JDBFlyException("[{}]已经存在", key);
        } else {
            JLinkedEntry<K, V> entry = new JLinkedEntry<K, V>(key, value);
            this.entry.setPrev(entry);
            this.entry = entry;
        }
    }

    /**
     * 尾部添加
     * 
     * @param key
     * @param value
     */
    public synchronized void addLast(K key, V value) {
        JAssert.notNull(key, "key不能为空");
        JAssert.notNull(value, "value不能为空");
        if (this.entry == null) {
            this.entry = new JLinkedEntry<K, V>(key, value);
        } else if (this.hasKey0(key)) {
            throw new JDBFlyException("[{}]已经存在", key);
        } else {
            this.filter0(item -> item.isTail()).setNext(new JLinkedEntry<K, V>(key, value));
        }
    }

    /**
     * 在指定元素之前添加
     * 
     * @param beforeKey
     * @param key
     * @param value
     */
    public synchronized void addBefore(K beforeKey, K key, V value) {
        JAssert.notNull(beforeKey, "beforeKey不能为空");
        JAssert.notNull(key, "key不能为空");
        JAssert.notNull(value, "value不能为空");
        if (this.entry == null) {
            throw new JDBFlyException("[{}]不存在", beforeKey);
        } else if (this.hasKey0(key)) {
            throw new JDBFlyException("[{}]已经存在", key);
        }
        JLinkedEntry<K, V> entry = this.getLinkedEntry(beforeKey);
        JLinkedEntry<K, V> current = new JLinkedEntry<K, V>(key, value);
        if (this.entry == entry) {
            this.entry.setPrev(current);
            this.entry = current;
        } else {
            JLinkedEntry<K, V> prev = entry.getPrev();
            prev.setNext(current);
            current.setNext(entry);
        }
    }

    /**
     * 在指定元素之后添加
     * 
     * @param afterKey
     * @param key
     * @param value
     */
    public synchronized void addAfter(K afterKey, K key, V value) {
        JAssert.notNull(afterKey, "afterKey不能为空");
        JAssert.notNull(key, "key不能为空");
        JAssert.notNull(value, "value不能为空");
        if (this.entry == null) {
            throw new JDBFlyException("[{}]不存在", afterKey);
        } else if (this.hasKey0(key)) {
            throw new JDBFlyException("[{}]已经存在", key);
        }
        JLinkedEntry<K, V> entry = this.getLinkedEntry(afterKey);
        JLinkedEntry<K, V> current = new JLinkedEntry<K, V>(key, value);
        JLinkedEntry<K, V> next = entry.getNext();
        entry.setNext(current);
        current.setNext(next);
    }

    /**
     * 移除指定元素
     * 
     * @param key
     * @return
     */
    public synchronized V remove(K key) {
        JAssert.notNull(key, "key不能为空");
        JLinkedEntry<K, V> entry = this.getLinkedEntry(key);
        if (this.entry == entry) {
            this.entry = entry.getNext();
        } else {
            entry.getPrev().setNext(entry.getNext());
        }
        return entry.getValue();
    }

    /**
     * 移除第一个元素
     * 
     * @return
     */
    public synchronized V removeFirst() {
        if (this.entry == null) {
            throw new JDBFlyException("未包含任何元素");
        }
        V value = this.entry.getValue();
        this.entry = this.entry.getNext();
        this.entry.setHead();
        return value;
    }

    /**
     * 移除最后一个元素
     * 
     * @return
     */
    public synchronized V removeLast() {
        if (this.entry == null) {
            throw new JDBFlyException("未包含任何元素");
        }
        JLinkedEntry<K, V> entry = this.filter0(item -> item.isTail());
        if (this.entry == entry) {
            this.entry = null;
        } else {
            entry.getPrev().setTail();
        }
        return entry.getValue();
    }

    /**
     * 替换指定元素
     * 
     * @param key
     * @param value
     */
    public synchronized void replace(K key, V value) {
        JAssert.notNull(key, "key不能为空");
        JAssert.notNull(value, "value不能为空");
        this.getLinkedEntry(key).setValue(value);
    }

    /**
     * 替换指定元素，如果元素不存在则添加到最后
     * 
     * @param key
     * @param value
     */
    public synchronized void replaceOrAddLast(K key, V value) {
        JAssert.notNull(key, "key不能为空");
        JAssert.notNull(value, "value不能为空");
        JLinkedEntry<K, V> wrapper = this.filter0(item -> item.getKey().equals(key));
        if (wrapper == null) {
            if (this.entry == null) {
                this.entry = new JLinkedEntry<K, V>(key, value);
            } else {
                this.filter0(item -> item.isTail()).setNext(new JLinkedEntry<K, V>(key, value));
            }
        } else {
            wrapper.setValue(value);
        }
    }

    /**
     * 获得第一个元素
     * 
     * @return
     */
    public synchronized V getFirst() {
        if (this.entry == null) {
            throw new JDBFlyException("未包含任何元素");
        }
        return this.entry.getValue();
    }

    /**
     * 获得最后一个元素
     * 
     * @return
     */
    public synchronized V getLast() {
        if (this.entry == null) {
            throw new JDBFlyException("未包含任何元素");
        }
        return this.filter0(item -> item.isTail()).getValue();
    }

    /**
     * 获得指定元素
     * 
     * @param key
     * @return
     */
    public synchronized V getEntry(K key) {
        JAssert.notNull(key, "key不能为空");
        return this.getLinkedEntry(key).getValue();
    }

    /**
     * 是否为空
     * 
     * @return
     */
    public synchronized boolean isEmpty() {
        return this.entry == null;
    }

    /**
     * 获得所有键
     * 
     * @return
     */
    public synchronized Set<K> getKeys() {
        Set<K> names = new LinkedHashSet<>();
        JLinkedEntry<K, V> current = this.entry;
        while (current != null) {
            names.add(current.getKey());
            current = current.getNext();
        }
        return names;
    }

    /**
     * 获得所有值
     * 
     * @return
     */
    public synchronized List<V> getValues() {
        List<V> values = new ArrayList<>();
        JLinkedEntry<K, V> current = this.entry;
        while (current != null) {
            values.add(current.getValue());
            current = current.getNext();
        }
        return values;
    }

    /**
     * 是否包含指定元素
     * 
     * @param key
     * @return
     */
    public synchronized boolean hasKey(K key) {
        return this.hasKey0(key);
    }

    /**
     * 是否包含指定元素
     * 
     * @param key
     * @return
     */
    private boolean hasKey0(K key) {
        return this.filter0(item -> item.getKey().equals(key)) != null;
    }

    /**
     * 获得指定元素
     * 
     * @param key
     * @return
     */
    private JLinkedEntry<K, V> getLinkedEntry(K key) {
        JLinkedEntry<K, V> wrapper = this.filter0(item -> item.getKey().equals(key));
        if (wrapper == null) {
            throw new JDBFlyException("[{}]不存在", key);
        }
        return wrapper;
    }

    /**
     * 过滤
     * 
     * @param predicate
     * @return
     */
    public synchronized JLinkedEntry<K, V> filter(Predicate<JLinkedEntry<K, V>> predicate) {
        return this.filter0(predicate);
    }

    /**
     * 过滤
     * 
     * @param predicate
     * @return
     */
    private JLinkedEntry<K, V> filter0(Predicate<JLinkedEntry<K, V>> predicate) {
        JLinkedEntry<K, V> current = this.entry;
        while (current != null) {
            if (predicate.test(current)) {
                return current;
            }
            current = current.getNext();
        }
        return null;
    }

    @Override
    public String toString() {
        return !this.isEmpty() ? this.entry.toString() : "Empty";
    }

    @Override
    public Iterator<JLinkedEntry<K, V>> iterator() {
        return new JItr();
    }

    private class JItr implements Iterator<JLinkedEntry<K, V>> {
        private JLinkedEntry<K, V> entry;

        public JItr() {
            this.entry = JLinkedList.this.entry;
        }

        @Override
        public boolean hasNext() {
            return this.entry != null;
        }

        @Override
        public JLinkedEntry<K, V> next() {
            if (!hasNext()) {
                throw new NoSuchElementException();
            }
            JLinkedEntry<K, V> next = this.entry;
            this.entry = this.entry.getNext();
            return next;
        }
    }
}
